perm filename MCTRST.PUB[HAL,HE]1 blob
sn#127026 filedate 1974-10-29 generic text, type T, neo UTF8
.NEWSS (CONTROL STRUCTURES,CONTROL STRUCTURES)
.NEWSSS TRADITIONAL ALGOL STRUCTURES
HAL has many of the traditional ALGOL control structures,
including the statement, the block, the IF-THEN-ELSE conditional,
the WHILE loop, the FOR loop, and the GOTO (which in HAL is written
JUMP. JUMPs will not be implemented at first, because they confuse
the flow analysis needed for maintaining planning values and because
it is possible to accomplish much without them). The simple
statement can be of several varieties: Assignment, declaration,
manipulation, assertions, condition monitoring. The
assignment statement is of the general form
.UNFILL
FROB α← A + (2,0,0) ,
.REFILL
that is, a variable name, a left arrow, and a suitable expression.
The types of expressions available will be discussed under the rubric
of data types. Declarations are allowed anywhere in the code that
other statements are allowed; this facilitates typing in a program
from a terminal. Manipulation, the fundamental purpose of HAL, will
be discussed fully in the section on motion specifications.
Assertions are discussed in {sssref asr}.
Condition monitoring is discussed in {sssref onm}.
A block is a list of statements separated by semicolons,
prefaced by the reserved word BEGIN and closed by the reserved word
END. Blocks are used to enclose a group of statements to form what
syntactically can act as one statement, and provide a means for
keeping variables local to a piece of code. It is possible to name a
block by inserting its name as a string after the BEGIN; this is
useful as a comment, and during debugging provides a way to name
blocks of code. When a block is so named, the name should be repeated
immediately after the END; this provides an easy way to insure proper
matching of BEGINs and ENDs. An example:
.UNFILL
BEGIN "SAMPLE"
SCALAR A, I;
A α← 2;
FOR I α← 1 STEP 1 UNTIL 10 DO A α← A*A;
WHILE A > 0 DO
BEGIN "LOOP"
A α← A - 1;
IF A < 5 THEN WRITE(A) ELSE WRITE(A-5)
END "LOOP";
WRITE("DONE")
END "SAMPLE"
.REFILL
.NEWSSS COBEGIN-COEND
In addition to these traditional structures, there are also
some special ones for specifying simultaneous independent execution.
The principal device for this is the COBEGIN-COEND pair, which
brackets statements whose execution is meant to begin independently.
Each of the statements within the COBEGIN block will eventually get
executed, with whatever overlapping of execution might occur during
runtime (for example, while one arm is moving, another statement can
be computing; several arms can also work at the same time). The termination
of the block occurs only when each of the statements in the scope of
the COBEGIN has terminated.
.tsk:NEWSSS PARTIAL ORDERING OF SUBTASKS
An assembly task is often divided into subtasks which form a
partial order with respect to the order of execution. An example is
a task A which contains four subtasks, B, C, D, and E, of which B and
C must be done before D, and D must be done before E, but B and C
could be done in any order. It is possible in HAL to leave the
ordering of the subtasks up to the compiler, which will try to
optimize the entire operation. The way to specify the example just
given is as follows:
.UNFILL
TASK BEGIN "A"
BEGIN "B"
<code for task B>
END "B";
BEGIN "C"
<code for task C>
END "C";
BEGIN "D AND E"
PREREQUISITE "B", "C";
<code for task D>
<code for task E>
END "D AND E"
END "A"
.REFILL
The words TASK BEGIN introduce a "task block", which contains
a set of statements. These statements may be blocks identified by
strings and may contain the declaration that certain other named
blocks within the same task are prerequisite for the inception of
this block.
The order in which the statements are performed is determined
only insofar as the prerequisite conditions demand. The compiler may
reorder them consistently with the preconditions, and may even execute
some of the statements simultaneously (as if there were a COBEGIN),
if this is feasible.
It is useful to place assertions at the beginning of the code
for any of the statements within a task; this assists the compiler in
maintaining its world model, and is also used to help decide the
proper ordering of the tasks. It is likewise good to place assertions
at the end of each statement. (Assertions are discussed in {sssref asr}.)
.NEWSSS EVENTS: SIGNAL AND WAIT
To achieve simultaneous coordinated motion, one uses a
special form of the move commands which will be discussed later.
However, some simple synchronization is possible within the context of
simultaneous execution. This is achieved by means of explicit
events, which can be signaled and awaited. For every type of event
that the user wishes to use, there should be a declaration something
like:
.UNFILL
EVENT E1, E2, E3 .
.REFILL
Each event initially has count 0, that is, no signals have appeared
for it, and no process is waiting for it. The statement
.UNFILL
SIGNAL E1
.REFILL
increments the count associated with event E1, and if the resulting
count is 0 or negative, one of those processes waiting for E1 is
freed from its wait and readied for execution. The statement
.UNFILL
WAIT E1
.REFILL
decrements the count associated with event E1, and if the resulting
count is negative, the process issuing the WAIT is blocked from
continuing until a signal comes along. If the count is 0 or
positive, there is no waiting.
An example of the utility of this construct is inside a
COBEGIN block, where one path of execution requires that the other
path has passed some milestone. Here is how such a use might appear:
.UNFILL
EVENT MILESTONE;
COBEGIN
BEGIN "PATH 1"
<code before the critical point>
WAIT MILESTONE;
<code after the critical point>
END "PATH 1";
BEGIN "PATH 2"
<code in preparation for the milestone>
SIGNAL MILESTONE;
<code following the milestone>
END "PATH 2"
COEND
.REFILL
.onm: NEWSSS ON MONITORS
It is often desired to monitor some set of conditions
throughout a section of code. A special kind of statement which allows
the user to do this is the ON statement. A simple example is
.UNFILL
ON T >5 DO STOP YELLOW
.REFILL
which while active will monitor the magnitude of T
and if it should become greater than 5 will cause the yellow arm to
stop, if it is moving. An ON monitor has two states: enabled and
disabled. Generally, an ON monitor will be enabled as soon as its
defining statement is executed, and it becomes disabled when its
block is exited. Also, as soon as an ON monitor triggers, it becomes
disabled until explicitly reenabled. Reenabling is done by executing
the statement ENABLE within the conclusion (that is, the statement
following the DO). It is possible to name ON monitors; a monitor
named "CH" will be enabled when a statement of the form ENABLE "CH"
is executed, and is disabled either under the conditions named above
or when it is explicitly turned off by a DISABLE statement. To have
an ON monitor initially disabled, preface the ON with the word DEFER.
The condition which is being tested must be a simple numeric
inequality or equality, possibly using some continually evaluated
function. Boolean combinations are not allowed.
Some examples of ON monitors:
.UNFILL
"FUDGE" ON TEMPERATURE > 400 DO WRITE("BURNING");
DEFER "WAIT" ON COOKED = 1 DO DISABLE "FUDGE";
ON MARK > 3 DO ENABLE "WAIT";
.REFILL
It should be noted that this ability to enable and disable
monitors explicitly is a non-structured concept; using it will often
lead to unintelligible programs. In any case, scope rules must be
observed; it is not legal to enable or disable a monitor in a
parallel or subsidiary block. This means that two statements,
simultaneously executing inside a COBEGIN block, are not allowed to
interfere with each other's ON monitors.
The conclusion of an ON-monitor may be any statement,
including an entire block. The only restriction is that if a motion
statement is the only statement in the conclusion, it must be
surrounded by BEGIN and END. (This is necessary at times to prevent
ambiguity.) Here is a slightly more complex example:
.UNFILL
ON DURATION > 5 DO
BEGIN
"BIND" ON FORCE(Z)>10 DO STOP YELLOW;
ON DIST > 3 DO
BEGIN
DISABLE BIND;
VEL α← 40
END
END
.REFILL
The existence of ON monitors raises this question: When is a
block considered to be finished? It can happen that all the
executable statements have finished, but some ON monitors remain.
This situation is often sterile; nothing can happen unless one of the
conditions happens to trigger, which may or may not ever occur.
Therefore, a block is declared finished when no interpreters
(that is, pieces of straightforward code, exclusive of on-monitors)
remain
active within it. This means that if even one ON monitor has caused
an interpreter to start executing (to perform the statements in the
conclusion), then the block is not exited until that interpreter is
done. But a block may be exited while some ON monitors are still
enabled; this automatically disables them.
The user must be wary of the relative speeds at which her
various pieces of code in ON test conclusions get executed. When an
ON test triggers, any initial statements of enabling or disabling are
done immediately, but any arithmetic is scheduled to be done at some
point in the near future. Therefore it is not possible to guarantee
that a critical computation happen immediately. If the user desires,
she may use the word CRITICAL at the start of the conclusion, and
UNCRITICAL at the start of that code which need not be guaranteed
immediate execution.
Only one occurrence of CRITICAL, at the very start of the conclusion,
and only one occurrence of UNCRITICAL are allowed.
HAL automatically assumes CRITICAL before
statements of enabling and disabling, and UNCRITICAL immediately
following.
An example:
.UNFILL
ON T>0 DO
BEGIN "CONCLUSION"
ENABLE "GOOD GUY"; COMMENT: Assumed CRITICAL;
T α← 3; COMMENT: Assumed UNCRITICAL;
END "CONCLUSION";
ON S<0 DO
BEGIN "RESULT"
CRITICAL; COMMENT: Overrides defaults;
T α← 4; COMMENT: Will be done immediately;
UNCRITICAL; COMMENT: End of critical region;
DISABLE "GOOD GUY"; COMMENT: Done eventually;
END "RESULT"
.REFILL
.NEWSSS UNITS
Numbers are quite often used for measurements, and many
different systems of measurement exist. The UNITS statement serves
to inform the compiler which units are being used. The compiler uses its own
system for internal consistency, and does any scaling when necessary.
The default units, which the compiler itself uses, are placed first
in the following lists:
.UNFILL
Time: milliseconds, seconds, jiffies (that is, thirds: 60ths
of a second), minutes.
(The grain of the runtime is on the order of jiffies).
Distance: centimeters, inches.
Mass: grams, ounces, pounds, kilograms.
Angles: radians, degrees.
Rotations: Euler, Bolles, Taylor, Geomed. (These are explained
in {sssref dat}.)
An example:
UNITS SECONDS, DEGREES, TAYLOR;
.REFILL
.NEWSSS COMMENTS
As in Algol, comments are prefaced with the word COMMENT and
terminated by a semicolon. Also, any text enclosed in curly brackets,
as in
.UNFILL
α{We are the hollow menα}
.REFILL
will be ignored. The user can optionally reset the comment delimiters
from "α{α}" to whatever she wishes, by means of a require statement
such as:
.UNFILL
REQUIRE "α%α%" COMMENT_DELIMITERS .
.REFILL
which would cause the scanner to ignore any text between "α%" signs.
.NEWSSS LABELS
A LABEL is a point in the program to which a jump may be
made. Labels are not declared. An
example:
.UNFILL
FOO: A α← A + 1;
IF A < 100 THEN JUMP FOO;
.REFILL
Labels are in general useful mainly for debugging, and not for
program control, especially since JUMP will not be implemented at
first.
.NEWSSS ABORT
Occasionally the user wishes to stipulate that if the program
ever reaches a particular point, something is hopelessly wrong. The
statement ABORT causes the runtime to stop all moving devices and
to terminate execution. The supervisor is informed of the halt, and
will inform the user. ABORT takes an optional string argument, which
is a message which will be given to the user if the ABORT statement
is executed. An example:
.UNFILL
ABORT ("I KEEP MISSING THE BOLT!")
.REFILL
.NEWSSS OUTPUT
There are several ways that the user can request output
from HAL to the console. As mentioned above, ABORT can print
a message during execution. There is one other way to print a
message during execution, the WRITE statement, which
takes as arguments a list of variables and constants.
It is also legal to include a string constant in this list (there
are no string variables in HAL). Formatting of output is automatic.
An example:
.UNFILL
WRITE ("I think that the pump is at ",PUMP)
.REFILL
Some pieces of code are only intended to work under
certain conditions of planning knowledge. Such code might
have a check to insure that its preconditions are met; if not,
it is proper to signal a compile-time error, with a message.
This is done with the COMPILE_ERROR statement, which optionally
takes a string argument, and which will halt compilation and print
the message. One of the options the supervisor will give the user
is to proceed as if no error had been encountered. Here is
an example:
.UNFILL
COMPILE_ERROR("Hey! You didn't attach the pump to the hand!")
.REFILL
A similar statement which merely prints its message but
does not halt compilation is the COMPILE_WRITE statement, which
behaves in all respects like the runtime WRITE statement, in that
it can take variables and constants in its argument list, but where
variables are specified, the planning values will be printed. For
example:
.UNFILL
COMPILE_WRITE("YELLOW arm should be at",YELLOW)
.REFILL
.NEWSSS PROCEDURES
HAL has only a limited capacity for procedures. All
parameters to a procedure assume the planning value "undefined" at
the conclusion of a procedure call, except those which are declared
as VALUE parameters in the procedure heading, or those stated to be
UNCHANGED in the procedure call. There is no safeguard against the
accidental modification of "unchanged" parameters; to state
"unchanged" is entirely equivalent to an assertion that the parameter
has not changed its value during the execution of the procedure. The
declaration of a procedure is this:
.UNFILL
type PROCEDURE name (argument list) ,
.REFILL
where "type" is any datatype (and is optional), and "argument list"
is a list of parameter names with their types. An example:
.UNFILL
SCALAR PROCEDURE LGTH (FRAME F1, F2; VECTOR VALUE V1);
.REFILL
This declares that LGTH is a procedure which returns a scalar, and
takes as arguments two frames and one vector. The vector is not
changed by the procedure.
To call such a procedure:
.UNFILL
S1 α← LGTH(FROB, UNCHANGED HOLE, VECT);
.REFILL
This further asserts that HOLE is not changed by the call.
Assertions are essential at the start of a procedure body to
inform the compiler of the values to expect for the various
arguments. Remember that trajectories planned on the basis of highly
inaccurate planning values will not work well.
Assertions are discussed in {sssref asr}.
As a procedure is entered, all variables have planning value
"undefined". Globals may be accessed, but they also have undefined
initial planning value. All variables which have explicit or
implicit assignments within a procedure acquire the value
"undefined" at the point directly after the procedure call.
No modification of the attach structure is allowed inside a
procedure. The compiler (often wrongly) assumes that there are no
attachments involving variables within a procedure; to override this,
.UNFILL
ASSERT FACT (ATTACHED F1 F2) .
.REFILL
Attachments are discussed in {ssref att}.
There are four special types of procedure calls: A HAL
program might wish to call a routine coded for the PDP11 or a routine
coded for the PDP10. Likewise, a program on the PDP10 may wish to
control a HAL program, or a routine on the PDP11 may wish to request
some arm motion.
To achieve the first two cases, there exist "external
procedures" in HAL. These are compiled into calls on either routines
in the PDP11 or routines in the runtime PDP10 package. To declare
such a procedure:
.UNFILL
EXTERNAL PDP10 FRAME PROCEDURE FOO (FRAME A, B; VECTOR V) .
.REFILL
This declares the procedure FOO to be a procedure resident
in the runtime PDP10 package, expected to return a frame value, and
taking as arguments two frames and a vector. PDP10 procedures do not
have access to the actual variables sent, since copies are made;
therefore, all arguments to PDP10 procedures are considered to be
VALUE parameters.
It is possible to declare untyped (i.e., statement-like,
instead of expression-like) procedures as well. Replace "PDP10" with
"PDP11" for procedures in the PDP11. PDP11 procedures do have access
to values, and therefore parameters are not automatically considered
to be VALUE.
To achieve the second two cases, there exist "internal
procedures" in HAL. It is not necessary to state from where the
procedure may be called. Internal procedures must be at the top
level of a HAL program. A complete HAL program is considered to be
an untyped procedure without parameters.